﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NRemedy;
using BMC.ARSystem;
using System.Diagnostics;
using System.Reflection;

namespace NRemedy.Test
{
    [TestClass]
    public class ModelBinder_Test
    {
        #region UnBind_Test
        [TestMethod]
        public void ModelBinder_UnBind_SearialTest()
        {
            //No property
            MockModel0 model0 = new MockModel0();
            ModelBinder<MockModel0> binder0 = new ModelBinder<MockModel0>();
            var result0 = binder0.UnBind(model0);
            Assert.AreEqual(result0.Count, 0);

            //two properties and two mappings
            MockModel1 model1 = new MockModel1();
            ModelBinder<MockModel1> binder1 = new ModelBinder<MockModel1>();
            var result1 = binder1.UnBind(model1);
            Assert.AreEqual(2, result1.Count);
            Assert.AreEqual("Value1", result1[1]);
            Assert.AreEqual("Value2", result1[2]);
            Assert.AreEqual((int)ARType.CharacterField, result1.GetValueType(1));
            Assert.AreEqual((int)ARType.CharacterField, result1.GetValueType(2));

            //one property but no mapping
            MockModel2 model2 = new MockModel2();
            ModelBinder<MockModel2> binder2 = new ModelBinder<MockModel2>();
            var result2 = binder2.UnBind(model2);
            Assert.AreEqual(result2.Count, 0);


            //two properties and two mappings but two id duplicate
            MockModel3 model3 = new MockModel3();
            ModelBinder<MockModel3> binder3 = new ModelBinder<MockModel3>();
            try
            {
                var result3 = binder3.UnBind(model3);
                Assert.AreEqual("DuplicateIDException", string.Empty);
            }
            catch(DuplicateIDException ex)
            {
                Assert.AreEqual(ex.ID, (uint)5366);
            }

            //one property with ARField but without DatabaseID,expect exception
            MockModel4 model4 = new MockModel4();
            ModelBinder<MockModel4> binder4 = new ModelBinder<MockModel4>();
            try
            {
                binder4.UnBind(model4);
                Assert.AreEqual("CustomAttributeFormatException", string.Empty);

            }
            catch (CustomAttributeFormatException ex)
            {
                Assert.AreEqual(string.Format("DatabaseID of ARFieldAttribute on Property : {0} is missing.", "Property1"), ex.Message);
            }

            //one property with ARField but without ARType,expect exception
            MockModel5 model5 = new MockModel5();
            ModelBinder<MockModel5> binder5 = new ModelBinder<MockModel5>();
            try
            {
                binder5.UnBind(model5);
                Assert.AreEqual("CustomAttributeFormatException", string.Empty);

            }
            catch (CustomAttributeFormatException ex)
            {
                Assert.AreEqual(string.Format("DataType of ARFieldAttribute on Property : {0} cannot be null.", "Property1"),ex.Message);
            }

            //different modelbinderaccesslevel
            MockModel6 model6 = new MockModel6();
            model6.Property1 = 1;
            model6.Property2 = 2;
            ModelBinder<MockModel6> binder6 = new ModelBinder<MockModel6>();
            var valueList = binder6.UnBind(model6);
            Assert.AreEqual(1, valueList.Count);
            Assert.AreEqual((double)1, valueList[1]);
            Assert.AreEqual((int)ARType.RealField, valueList.GetValueType(1));
        }

        [TestMethod]
        public void ModelBinder_UnBind_SearialTest2() 
        {
            ModelBinder<MockModel7> binder = new ModelBinder<MockModel7>();
            MockModel7 model = new MockModel7();
            model.IntProperty = 1;
            model.RealProperty = 1.1;
            model.StringProperty = "1";
            model.EnumProperty = MockModel7.EnumProperty_Enum.Close;
            model.DiaryPropertySet = "dd";
            model.DecimalProperty = 1.1m;
            model.DateTimeProperty = new DateTime(2012, 1, 1, 1, 1, 1);
            model.CurrencyProperty = new CurrencyValue(1.1m, "USD");
            model.AttachmentProperty = new Attachment(){ Name = "att" };
            model.DateOnlyProperty = new DateValue(new DateTime(2012, 1, 1, 1, 1, 1));
            model.TimeOnlyProperty = new TimeOfDayValue(1, 1, 1);

            FieldValueList fvl = binder.UnBind(model);
            Assert.AreEqual(1, (int)fvl[1]);
            Assert.AreEqual(1.1, (double)fvl[2]);
            Assert.AreEqual("1", (string)fvl[3]);
            Assert.AreEqual("dd", (string)fvl[4]);
            Assert.AreEqual((int)MockModel7.EnumProperty_Enum.Close, (int)((object[])fvl[5])[1]);
            Assert.AreEqual(new DateTime(2012, 1, 1, 1, 1, 1).ToUniversalTime(), (DateTime)fvl[6]);
            Assert.AreEqual(1.1m, (decimal)fvl[7]);
            Assert.AreEqual("att", ((Attachment)fvl[8]).Name);
            Assert.AreEqual("USD", ((CurrencyValue)fvl[9]).CurrencyCode);
            Assert.AreEqual(1.1m, ((CurrencyValue)fvl[9]).Value);
            Assert.AreEqual(1, ((DateValue)fvl[10]).Month);
            Assert.AreEqual(3661, ((TimeOfDayValue)fvl[11]).TotalSeconds);

        }
        [TestMethod]
        public void ModelBinder_UnBind_SearialTest3()
        {
            ModelBinder<MockModel9> binder = new ModelBinder<MockModel9>();
            MockModel9 model = new MockModel9();
            model.IntProperty = 1;
            model.StringProperty = "1";
            model.EnumProperty = MockModel9.EnumProperty_Enum.Close;
            model.DateTimeProperty = new DateTime(2012, 1, 1, 1, 1, 1);
            PropertyInfo[] properties = model.GetType().GetProperties();
            foreach (var pi in properties)
            {
                //str = string.Concat(str, "|", pi.Name, ":", pi.GetValue(t, null));
                  var fieldAttribute = binder.GetARAttributeField(pi, ModelBinderAccessLevel.OnlyBind);
                  binder.ARConvertToNET(pi.PropertyType, fieldAttribute.DataType, pi.GetValue(model, null));
            }

         
          
          
           
           

        }

        private class MockModel0
        {

        }
        private class MockModel1
        {
            [ARField(DatabaseID=1, DatabaseName="Property1", DataType= ARType.CharacterField)]
            public string Property1 { get; set; }

            [ARField(DatabaseID = 2, DatabaseName = "Property2", DataType = ARType.CharacterField)]
            public string Property2 { get; set; }


            [ARField(DatabaseID = 2, DatabaseName = "Property2", DataType = ARType.CharacterField)]
            private string Property3 { get; set; }


            [ARField(DatabaseID = 2, DatabaseName = "Property2", DataType = ARType.CharacterField)]
            private string Property4 { set { tt = value; } }

            private string tt;

            public MockModel1()
            {
                Property1 = "Value1";
                Property2 = "Value2";
            }
 
        }
        private class MockModel2
        {
            public string Property1 { get; set; }

        }
        private class MockModel3
        {
            [ARField(5366, "Property1", ARType.CharacterField)]
            public string Property1 { get; set; }

            [ARField(5366, "Property2", ARType.CharacterField)]
            public string Property2 { get; set; }

        }
        private class MockModel4
        {
            [ARField]
            public string Property1 { get; set; }

        }
        private class MockModel5
        {
            [ARField(DatabaseID=54)]
            public string Property1 { get; set; }

        }
        private class MockModel6
        {
            [ARField(DatabaseID = 1, DatabaseName="", DataType = ARType.RealField, BinderAccess = ModelBinderAccessLevel.OnlyUnBind)]
            public double Property1 { get; set; }

            [ARField(DatabaseID = 1, DataType = ARType.RealField, BinderAccess = ModelBinderAccessLevel.OnlyBind)]
            public double Property2 { get; set; }

        }
        //for st2
        private class MockModel7
        {

            public enum EnumProperty_Enum
            {
                Active,
                Close,
                Reject,
                Assign
            }

            [ARField(DatabaseID = 1, DataType = ARType.IntegerField)]
            public int IntProperty { get; set; }

            [ARField(DatabaseID = 2, DataType = ARType.RealField)]
            public double RealProperty { get; set; }

            [ARField(DatabaseID = 3, DataType = ARType.CharacterField)]
            public string StringProperty { get; set; }

            [ARField(DatabaseID = 4, DataType = ARType.DiaryField,BinderAccess=ModelBinderAccessLevel.OnlyUnBind)]
            public string DiaryPropertySet { get; set; }

            [ARField(DatabaseID = 4, DataType = ARType.DiaryField, BinderAccess = ModelBinderAccessLevel.OnlyBind)]
            public DiaryList DiaryPropertyGet { get; private set; }

            [ARField(DatabaseID = 5, DataType = ARType.SelectionField)]
            public EnumProperty_Enum EnumProperty { get; set; }

            [ARField(DatabaseID = 6, DataType = ARType.DateTimeField)]
            public DateTime DateTimeProperty { get; set; }

            [ARField(DatabaseID = 7, DataType = ARType.DecimalField)]
            public decimal DecimalProperty { get; set; }

            [ARField(DatabaseID = 8, DataType = ARType.AttachmentField)]
            public Attachment AttachmentProperty { get; set; }

            [ARField(DatabaseID = 9, DataType = ARType.CurrencyField)]
            public CurrencyValue CurrencyProperty { get; set; }

            [ARField(DatabaseID = 10, DataType = ARType.DateOnlyField)]
            public DateValue DateOnlyProperty { get; set; }

            [ARField(DatabaseID = 11, DataType = ARType.TimeOnlyField)]
            public TimeOfDayValue TimeOnlyProperty { get; set; }



        }

        //for st3
        private class MockModel9
        {

            public enum EnumProperty_Enum
            {
                Active,
                Close,
                Reject,
                Assign
            }

            [ARField(DatabaseID = 1, DataType = ARType.IntegerField)]
            public Nullable<int> IntProperty { get; set; }

            [ARField(DatabaseID = 2, DataType = ARType.CharacterField)]
            public string StringProperty { get; set; }

            [ARField(DatabaseID = 3, DataType = ARType.SelectionField)]
            public Nullable<EnumProperty_Enum> EnumProperty { get; set; }

            [ARField(DatabaseID = 4, DataType = ARType.DateTimeField)]
            public Nullable<DateTime> DateTimeProperty { get; set; }




        }

        #endregion


        #region ValueConvert_Test

        [TestMethod]
        public void ValueConvert_SearialTest()
        {
            ModelBinder<MockModel5> binder = new ModelBinder<MockModel5>();

            //ARType = ARType.None
            try
            {
                binder.NETConvertToAR("s", ARType.None);
                Assert.AreEqual("ArgumentNullException", string.Empty);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Assert.AreEqual("type", ex.ParamName);
            }

            //.net value not match ar type
            try
            {
                binder.NETConvertToAR("s", ARType.DateOnlyField);
                Assert.AreEqual("InvalidCastException", string.Empty);
            }
            catch (InvalidCastException ex)
            {
                Assert.AreEqual(string.Format("propValue : {0} cannot cast to the ARType : {1}", "s", "DateOnlyField"), ex.Message);
            }

            //normal
            var intvalue = binder.NETConvertToAR(1234, ARType.IntegerField);
            Assert.AreEqual(1234, intvalue);
            var doublevalue = binder.NETConvertToAR(1.234, ARType.RealField);
            Assert.AreEqual(1.234, doublevalue);
            var stringvalue = binder.NETConvertToAR("s", ARType.CharacterField);
            Assert.AreEqual("s", stringvalue);
            var stringvalue2 = binder.NETConvertToAR("ss", ARType.DiaryField);
            Assert.AreEqual("ss", stringvalue2);
            var intvalue2 = binder.NETConvertToAR(200, ARType.SelectionField);
            Assert.AreEqual(200, intvalue2);
            var datetimevalue1 = binder.NETConvertToAR(new DateTime(2012, 04, 16, 13, 13, 59), ARType.DateTimeField);
            Assert.AreEqual(TimeZone.CurrentTimeZone.ToUniversalTime(new DateTime(2012, 04, 16, 13, 13, 59)), datetimevalue1);
            var decimalvalue = binder.NETConvertToAR(2.34m, ARType.DecimalField);
            Assert.AreEqual(2.34m, decimalvalue);
            var attachvalue = binder.NETConvertToAR(new Attachment() { Name = "att.xls" }, ARType.AttachmentField);
            Assert.AreEqual("att.xls", (attachvalue as Attachment).Name);
            var currencyvalue = binder.NETConvertToAR(new CurrencyValue(9.99m, "USD"), ARType.CurrencyField);
            Assert.AreEqual("USD", (currencyvalue as CurrencyValue).CurrencyCode);
            var datetimevalue2 = binder.NETConvertToAR(new DateValue(2012,4,1), ARType.DateOnlyField);
            Assert.AreEqual(new DateValue(2012, 4, 1).JulianDate, (int)datetimevalue2);
            var intvalue3 = binder.NETConvertToAR(new TimeOfDayValue(15, 4, 16), ARType.TimeOnlyField);
            Assert.AreEqual(54256, intvalue3);
        }

        #endregion

        #region Bind_Test

        [TestMethod]
        public void ModelBinder_Bind_SearialTest()
        { 
            //Argument null
            ModelBinder<MockModel00_With_No_Property> binder = new ModelBinder<MockModel00_With_No_Property>();
            try
            {
                binder.Bind(null);
                Assert.AreEqual("ArgumentNullException", string.Empty);
            }
            catch(ArgumentNullException anex)
            {
                Assert.AreEqual("valueList", anex.ParamName);
 
            }

            //model no property
            FieldValueList fvl1 = new FieldValueList();
            fvl1[1] = 1;
            fvl1[2] = 1.1;
            binder.Bind(fvl1);

            //model's property more than valueList
            ModelBinder<MockModel00_With_3_Property> binder2 = new ModelBinder<MockModel00_With_3_Property>();
            try
            {
                binder2.Bind(fvl1);
                Assert.AreEqual("InvalidCastException", string.Empty);
            }
            catch(InvalidCastException incex)
            {
                Assert.AreEqual(string.Format("valueList cannot cast to model : {0}", typeof(MockModel00_With_3_Property).FullName), incex.Message);
 
            }
            //model's property less than valueList
            fvl1[3] = "1";
            fvl1[4] = 1;
            MockModel00_With_3_Property model = binder2.Bind(fvl1);
            Assert.AreEqual(1, model.Property1);
            Assert.AreEqual(1.1, model.Property02);
            Assert.AreEqual("1", model.Property03);

            //MockModel8 Test and include UnBindForUpdate test
            FieldValueList fvl = new FieldValueList();
            fvl[1] = 1;
            fvl[2] = 1.1;
            fvl[3] = "1";
            fvl[4] = new DiaryList();
            fvl[5] = 0;
            fvl[6] = new DateTime(2012, 1, 1, 1, 1, 1).ToUniversalTime();
            fvl[7] = 1.1m;
            fvl[8] = new Attachment() { Name="att" };
            fvl[9] = new CurrencyValue(1.1m, "USD");
            //TODO:cannot test below cast
            //fvl.Add(10, new DateValue(2012, 1, 1));
            //fvl.Add(11, new TimeOfDayValue(1, 1, 1));

            ModelBinder<MockModel8> binder8 = new ModelBinder<MockModel8>();
            MockModel8 model8 = binder8.Bind(fvl);
            Assert.AreEqual(1, model8.IntProperty);
            Assert.AreEqual(1.1, model8.RealProperty);
            Assert.AreEqual("1", model8.StringProperty);
            Assert.AreEqual(typeof(DiaryList), model8.DiaryPropertyGet.GetType());
            Assert.AreEqual(MockModel8.EnumProperty_Enum.Active, model8.EnumProperty);
            Assert.AreEqual(new DateTime(2012, 1, 1, 1, 1, 1), model8.DateTimeProperty);
            Assert.AreEqual(1.1m, model8.DecimalProperty);
            Assert.AreEqual("att", model8.AttachmentProperty.Name);
            Assert.AreEqual("USD", model8.CurrencyProperty.CurrencyCode);

            model8.StringProperty = "2";
            var fvlUpdated = binder8.UnBindForUpdate(model8);
            Assert.AreEqual(1, fvlUpdated.Count);
            Assert.AreEqual("2", fvlUpdated[3]);

        }

        [TestMethod]
        public void ModelBinder_Bind_SearialTest2()
        {
            //error mapped property
            ModelBinder<MockModel00_With_ErrorMapped_Property> binder = new ModelBinder<MockModel00_With_ErrorMapped_Property>();
            FieldValueList fvl1 = new FieldValueList();
            fvl1[1] = 1;
            fvl1[2] = 1.1;
            fvl1[3] = "1.1";
            try
            {
                binder.Bind(fvl1);
                Assert.AreEqual("InvalidCastException", string.Empty);
            }
            catch(InvalidCastException ex0)
            {
                Assert.AreEqual(
                    string.Format(
                    "ARValue : {0} with type of {1} cannot cast to the NETType : {2}", 1, ARType.IntegerField, typeof(String)),
                    ex0.Message);
            }
 
            //valueList contains DBNull
            ModelBinder<MockModel00_With_3_Property> binder2 = new ModelBinder<MockModel00_With_3_Property>();
            FieldValueList fvl2 = new FieldValueList();
            fvl2[1] = System.DBNull.Value;
            fvl2[2] = System.DBNull.Value;
            fvl2[3] = System.DBNull.Value;
            MockModel00_With_3_Property model2 = binder2.Bind(fvl2);

        }

        private class MockModel00_With_No_Property
        {
            public string Property1 { get; set; }

            public string Property02 { get; set; }
        }
        private class MockModel00_With_3_Property
        {
            [ARField(1, "Property1", ARType.IntegerField)]
            public int Property1 { get; set; }

            [ARField(2, "Property2", ARType.RealField)]
            public double Property02 { get; set; }

            [ARField(3, "Property3", ARType.CharacterField)]
            public string Property03 { get; set; }
        }
        private class MockModel00_With_ErrorMapped_Property
        {
            [ARField(1, "Property1", ARType.IntegerField)]
            public string Property1 { get; set; }

            [ARField(2, "Property2", ARType.RealField)]
            public double Property02 { get; set; }

            [ARField(3, "Property3", ARType.CharacterField)]
            public string Property03 { get; set; }
        }
        private class MockModel8 : ARBaseForm
        {

            public enum EnumProperty_Enum
            {
                Active,
                Close,
                Reject,
                Assign
            }

            private string _StringProperty;

            [ARField(DatabaseID = 1, DataType = ARType.IntegerField)]
            public int IntProperty { get; set; }

            [ARField(DatabaseID = 2, DataType = ARType.RealField)]
            public double RealProperty { get; set; }

            [ARField(DatabaseID = 3, DataType = ARType.CharacterField)]
            public string StringProperty {
                get 
                {
                    return _StringProperty;
                }
                set
                {
                    _StringProperty = value;
                    OnPropertyChanged("StringProperty");
 
                }
            }

            [ARField(DatabaseID = 4, DataType = ARType.DiaryField, BinderAccess = ModelBinderAccessLevel.OnlyUnBind)]
            public string DiaryPropertySet { get; set; }

            [ARField(DatabaseID = 4, DataType = ARType.DiaryField, BinderAccess = ModelBinderAccessLevel.OnlyBind)]
            public DiaryList DiaryPropertyGet { get; private set; }

            [ARField(DatabaseID = 5, DataType = ARType.SelectionField)]
            public EnumProperty_Enum EnumProperty { get; set; }

            [ARField(DatabaseID = 6, DataType = ARType.DateTimeField)]
            public DateTime DateTimeProperty { get; set; }

            [ARField(DatabaseID = 7, DataType = ARType.DecimalField)]
            public decimal DecimalProperty { get; set; }

            [ARField(DatabaseID = 8, DataType = ARType.AttachmentField)]
            public Attachment AttachmentProperty { get; set; }

            [ARField(DatabaseID = 9, DataType = ARType.CurrencyField)]
            public CurrencyValue CurrencyProperty { get; set; }

        }

        #endregion
    }

}
